package ua.naiksoftware.stompclientexample.rxHarmony;

import io.reactivex.Scheduler;
import io.reactivex.disposables.Disposable;
import io.reactivex.disposables.Disposables;
import io.reactivex.plugins.RxJavaPlugins;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.InnerEvent;

import java.util.concurrent.TimeUnit;

/**
 * HandlerScheduler
 *
 * @since 2021-04-27
 */
final class OpenHandlerScheduler extends Scheduler {

    private static final int INNER_EVENT_ID = 9819;
    private final EventHandler handler;

    /**
     * HandlerWorker
     *
     * @since 2021-04-27
     */
    private static final class HandlerWorker extends Worker {
        private final EventHandler handler;
        private volatile boolean isDisposed;

        HandlerWorker(EventHandler eventHandler) {
            this.handler = eventHandler;
        }

        @Override
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            if (run == null) {
                throw new NullPointerException("run == null");
            }
            if (unit == null) {
                throw new NullPointerException("unit == null");
            }

            if (isDisposed) {
                return Disposables.disposed();
            }
            ScheduledRunnable scheduled = new ScheduledRunnable(handler, RxJavaPlugins.onSchedule(run));

            InnerEvent.get(INNER_EVENT_ID, 0, null);

            handler.postTask(scheduled, unit.toMillis(delay));

            /**
             * Re-check disposed state for removing in case we were racing a call to dispose().
             */
            if (isDisposed) {
                handler.removeAllEvent();
                return Disposables.disposed();
            }

            return scheduled;
        }

        @Override
        public void dispose() {
            isDisposed = true;
            handler.removeAllEvent();
        }

        @Override
        public boolean isDisposed() {
            return isDisposed;
        }
    }

    /**
     * ScheduledRunnable
     *
     * @since 2021-04-27
     */
    private static final class ScheduledRunnable implements Runnable, Disposable {
        private final EventHandler handler;
        private final Runnable delegate;

        private volatile boolean isDisposed;

        ScheduledRunnable(EventHandler eventHandler, Runnable runnable) {
            this.handler = eventHandler;
            this.delegate = runnable;
        }

        @Override
        public void run() {
            delegate.run();
        }

        @Override
        public void dispose() {
            isDisposed = true;
            handler.removeAllEvent();
        }

        @Override
        public boolean isDisposed() {
            return isDisposed;
        }
    }

    OpenHandlerScheduler(EventHandler eventHandler) {
        this.handler = eventHandler;
    }

    @Override
    public Disposable scheduleDirect(Runnable run, long delay, TimeUnit unit) {
        if (run == null) {
            throw new NullPointerException("run == null");
        }
        if (unit == null) {
            throw new NullPointerException("unit == null");
        }
        ScheduledRunnable scheduled = new ScheduledRunnable(handler, RxJavaPlugins.onSchedule(run));
        handler.postTask(scheduled, unit.toMillis(delay));
        return scheduled;
    }

    @Override
    public Worker createWorker() {
        return new HandlerWorker(handler);
    }
}
